package com.simple.gateway.assist.application;

import cn.hutool.core.collection.CollectionUtil;
import com.simple.gateway.assist.config.GatewayServiceProperties;
import com.simple.gateway.assist.service.RegisterGatewayService;
import com.simple.gateway.assist.service.bo.ApplicationInterfaceBO;
import com.simple.gateway.assist.service.bo.ApplicationInterfaceMethodBO;
import com.simple.gateway.assist.service.bo.ApplicationSystemBO;
import com.simple.gateway.assist.service.bo.ApplicationSystemRichBO;
import com.simple.gateway.assist.service.param.RegisterGatewayParam;
import com.simple.gateway.core.datasource.DataSourceType;
import com.simple.gateway.core.mapping.HttpCommandType;
import com.simple.gateway.core.mapping.HttpStatement;
import com.simple.gateway.core.session.Configuration;
import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;

import java.util.List;
import java.util.Objects;

/**
 * 项目: simple-gateway
 * <p>
 * 功能描述:
 *
 * @author: WuChengXing
 * @create: 2023-08-07 21:36
 **/
public class GatewayApplication implements ApplicationContextAware, ApplicationListener<ContextClosedEvent> {

    private final Logger logger = LoggerFactory.getLogger(GatewayApplication.class);

    private final GatewayServiceProperties properties;
    private final RegisterGatewayService registerGatewayService;
    private final Configuration configuration;
    private final Channel gatewaySocketServerChannel;

    public GatewayApplication(GatewayServiceProperties properties, RegisterGatewayService registerGatewayService,
                              Configuration configuration, Channel gatewaySocketServerChannel) {
        this.properties = properties;
        this.registerGatewayService = registerGatewayService;
        this.configuration = configuration;
        this.gatewaySocketServerChannel = gatewaySocketServerChannel;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        try {
            // 1. 注册网关服务；每一个用于转换 HTTP 协议泛化调用到 RPC 接口的网关都是一个算力，这些算力需要注册网关配置中心
            registerGatewayService.doRegister(properties.getAddress(), RegisterGatewayParam.builder()
                    .groupId(properties.getGroupId())
                    .gatewayAddress(properties.getGatewayAddress())
                    .gatewayId(properties.getGatewayId())
                    .gatewayName(properties.getGatewayName())
                    .build());
            // 加载所有的数据
            addMappers("");
        } catch (Exception e) {
            logger.error("网关服务启动失败，停止服务。{}", e.getMessage(), e);
        }
    }

    public void addMappers(String systemId) {
        // 2. 拉取网关配置；每个网关算力都会在注册中心分配上需要映射的RPC服务信息，包括；系统、接口、方法
        ApplicationSystemRichBO applicationSystemRichInfo = registerGatewayService.pullApplicationSystemRichInfo(properties.getAddress(), properties.getGatewayId(), systemId);
        if (Objects.isNull(applicationSystemRichInfo)) {
            logger.warn("网关{}服务注册映射为空，请排查 gatewayCenterService.pullApplicationSystemRichInfo 是否检索到此网关算力需要拉取的配置数据。", systemId);
            return;
        }
        List<ApplicationSystemBO> applicationSystemBOList = applicationSystemRichInfo.getApplicationSystemBOS();
        if (CollectionUtil.isEmpty(applicationSystemBOList)) {
            logger.warn("网关{}服务注册映射为空，请排查 gatewayCenterService.pullApplicationSystemRichInfo 是否检索到此网关算力需要拉取的配置数据。", systemId);
            return;
        }
        for (ApplicationSystemBO system : applicationSystemBOList) {
            List<ApplicationInterfaceBO> interfaceList = system.getApplicationInterfaceBOS();
            for (ApplicationInterfaceBO itf : interfaceList) {
                // 2.1 创建配置信息加载注册
                this.register(DataSourceType.valueOf(system.getSystemType()), system, itf);
                List<ApplicationInterfaceMethodBO> methodList = itf.getApplicationInterfaceMethodBOS();
                // 2.2 注册系统服务接口信息
                for (ApplicationInterfaceMethodBO method : methodList) {
                    HttpStatement httpStatement = new HttpStatement(
                            system.getSystemId(),
                            itf.getInterfaceId(),
                            method.getMethodId(),
                            method.getParameterType(),
                            method.getUri(),
                            HttpCommandType.valueOf(method.getHttpCommandType()),
                            method.isAuth(),
                            DataSourceType.valueOf(system.getSystemType())
                    );
                    configuration.addMapper(httpStatement);
                    logger.info("网关服务注册映射 系统：{} 接口：{} 方法：{}", system.getSystemId(), itf.getInterfaceId(), method.getMethodId());
                }
            }
        }
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        try {
            if (gatewaySocketServerChannel.isActive()) {
                logger.info("应用容器关闭，Api网关服务关闭。localAddress：{}", gatewaySocketServerChannel.localAddress());
                gatewaySocketServerChannel.close();
            }
        } catch (Exception e) {
            logger.error("应用容器关闭，Api网关服务关闭失败", e);
        }
    }

    private void register(DataSourceType dataSourceType, ApplicationSystemBO systemBO, ApplicationInterfaceBO itf) {
        switch (dataSourceType) {
            case HTTP:
                break;
            case RPC_DUBBO:
                // 创建dubbo泛化调用基本信息
                configuration.registryConfig(systemBO.getSystemId(), systemBO.getSystemRegistry(), itf.getInterfaceId(), itf.getInterfaceVersion());
                break;
            case DB_MYSQL:
                // 创建数据源信息
                configuration.registryDataSource(systemBO.getSystemId(), systemBO.getSystemRegistry(), systemBO.getExtension());
                break;
            default:
                throw new RuntimeException("DataSourceType：" + dataSourceType + "没有对应的数据源实现");
        }
    }

    public void receiveMessage(Object message) {
        logger.info("【事件通知】接收注册中心推送消息 message：{}", message);
        addMappers(message.toString().substring(1, message.toString().length() - 1));
    }

}
